/**
 * 
 */
package edu.caltech.android.sensor;

import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;

/**
 * Wraps up the GPS and network localization. To be safe, call stop() when this
 * is no longer needed.
 * 
 * An idea is to use LocationListeners to get updates. This class would then
 * need to manage registering and unregistering these listeners (e.g. one for
 * GPS, another for Network) in order to conserve power while obtaining a
 * desired level of accuracy.
 * 
 * requires uses_permissions android.permission.ACCESS_FINE_LOCATION, and maybe
 * android.permission.INTERNET
 * 
 * TODO how to handle case when no location can be obtained? I'd like to avoid
 * returning nulls. Should the last location fix be returned? Location objects
 * can be made via: Location l = new Location("dummyGPS"); maybe return location
 * objects, with very high position uncertainty?
 * 
 * 
 * @author mfaulk
 * 
 */
public class Localizer {
	private static String TAG = "Localizer";
	private LocationManager locationManager;
	private LocalizerListener networkLocationListener;

	public Localizer(Context context) {
		final String serviceString = Context.LOCATION_SERVICE;

		locationManager = (LocationManager) context
				.getSystemService(serviceString);

		if (locationManager == null) {
			System.err.println("Could not get a location manager");
			// TODO should throw an exception
		}

		// Oddly, registering this seems to make calls to getLastKnownLocation
		// work.
		// TODO
		networkLocationListener = new LocalizerListener();
		locationManager
				.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,
						networkLocationListener);
	}

	/**
	 * 
	 * @param criteria
	 * @return Location, or null
	 */
	public Location getLocation(Criteria criteria) throws SecurityException,
			IllegalArgumentException {
		String bestProvider = locationManager.getBestProvider(criteria, true);
		if (bestProvider == null) {
			System.err.println("no location providers. Did you enable them?");
		}
		Location location = locationManager.getLastKnownLocation(bestProvider);
		return location;
	}

	/**
	 * Attempt to obtain a location by GPS, then Network.
	 * 
	 * @return Location, or null if a location could not be obtained.
	 */
	public Location getLocation() {
		Location location = null;

		try {
			location = locationManager
					.getLastKnownLocation(LocationManager.GPS_PROVIDER);
			if (location == null) {
				location = locationManager
						.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
			}

			if (location != null) {
				Log.v(TAG, "Latitude: "
						+ Double.toString(location.getLatitude())
						+ ", Longitude: "
						+ Double.toString(location.getLongitude())
						+ ", Fix Accuracy: " + location.getAccuracy());
			} else {
				Log.d(TAG, "Could not obtain location.");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return location;
	}

	/**
	 * Release resources, unregister listeners, etc.
	 */
	public void stop() {
		locationManager.removeUpdates(networkLocationListener);
	}

	public boolean gpsEnabled() {
		return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
	}

	public boolean networkPositionEnabled() {
		return locationManager
				.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
	}

	/**
	 * 
	 * @author mfaulk
	 * 
	 */
	private class LocalizerListener implements LocationListener {

		@Override
		public void onLocationChanged(Location location) {
			System.out.println("location changed");
		}

		@Override
		public void onProviderDisabled(String provider) {
		}

		@Override
		public void onProviderEnabled(String provider) {
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
		}

	}

}
